---
title: createStreamableUI
layout:
  toc: false
---

import { Tabs, Tab } from 'nextra-theme-docs';

# createStreamableUI

## `createStreamableUI`

Create a piece of changable UI that can be streamed to the client. On the client side, it can be rendered as a normal React node.

## Parameters

Any valid `React.ReactNode` can be passed in such as `string` or `array`. But it will always be a UI node as a result and the client side can only render it, but not deconstructing it. Think of `createStreamableUI('hello')` gives you `<>hello<>`.

## Returns

This creates a special value that can be returned from Actions to the client. It holds a UI node inside and can be updated via the `update` method.

## Methods

### `update`

The `update` method is used to update the UI node. It takes a new UI node and replaces the old one.

### `append`

The `append` method is used to append a new UI node to the end of the old one. Once appended a new UI node, the previous UI node cannot be updated anymore.

### `done`

The `done` method marks the UI node as finalized. You can either call it without any parameters or with a new UI node as the final state. Once called, the UI node cannot be updated or appended anymore.

The `done` method is always **required** to be called, otherwise the response will be stuck in a loading state.

## Example

<Tabs items={['Next.js (App Router)']}>
  <Tab>
    UI Streams are created on the server and streamed to the client.

    ```tsx filename="app/action.tsx" {8-15, 23-30, 34-41, 46}
    import { createStreamableUI } from "ai/rsc";

    async function confirmPurchase(symbol: string, amount: number) {
      "use server";

      const price = getStockPrice(symbol);

      const uiStream = createStreamableUI(
        <div className="inline-flex gap-1">
          {spinner}
          <p className="mb-2">
            Purchasing {amount} ${symbol}...
          </p>
        </div>
      );

      // This async function is immediately invoked but it will not block the
      // return statement. Because of that, the client will receive the initial
      // UI immediately and then the updates will be streamed later.
      (async () => {
        await sleep(1000);

        uiStream.update(
          <div className="inline-flex gap-1">
            {spinner}
            <p className="mb-2">
              Purchasing {amount} ${symbol}... working on it...
            </p>
          </div>
        );

        await sleep(1000);

        uiStream.done(
          <div>
            <p className="mb-2">
              You have successfully purchased {amount} ${symbol}.
              Total cost: ${amount * price}
            </p>
          </div>
        );
      })();

      return {
        id: Date.now(),
        display: uiStream.value,
      }
    }
    ```

  </Tab>
</Tabs>
